
import { _decorator, Component, Node, Prefab, instantiate, LabelComponent, ScrollView, UITransform, NodePool, Rect, rect, v2, v3, UIOpacity } from 'cc';
import { LIstItem } from './ListItem';
const { ccclass, property, type, requireComponent } = _decorator;

@ccclass('ListView')
@requireComponent(ScrollView)
export class ListView extends Component {

    @type(Prefab)
    listItemPrefab: Prefab = null!;

    @type(Node)
    listContentNd: Node = null!;

    @type(Node)
    listLoading: Node = null!;

    @property
    maxRenderCount: number = 0;

    private _scrollViewCollisonBox: Rect = null!;
    private _start: number = 0;
    private _end: number = 0;
    private _isUpdating: boolean = false;
    private _listItemCount: number = 0;
    private _listRenderCount: number = 0;
    private _itemPool: NodePool = new NodePool();
    private _dataSource: Array<any> = null!;

    start() {
        // 获取 ScrollView Node 的左下角坐标在世界坐标系中的坐标
        const selfTransform: UITransform = this.getComponent(UITransform)!;
        const parentTransform: UITransform = this.node.parent?.getComponent(UITransform)!;
        const basePoint = parentTransform.convertToWorldSpaceAR(v3(
            this.node.position.x - selfTransform.anchorX * selfTransform.width,
            this.node.position.y - selfTransform.anchorY * selfTransform.height,
            0)
        );

        //可视区域在世界坐标系中的矩形（碰撞盒）
        this._scrollViewCollisonBox = rect(
            basePoint.x,
            basePoint.y,
            selfTransform.width,
            selfTransform.height
        );
    }

    onEnable() {
        this.node.on('scrolling', this.onScrolling, this);
    }

    private onScrolling() {
        if (this._isUpdating || this._listItemCount == 0) return;

        this.listContentNd.children.forEach((child: Node) => {
            const visible = this.checkVisible(child);
            child.getComponent(UIOpacity)!.opacity = visible ? 255 : 0;
        });
    }

    onDisable() {
        this.node.off('scrolling', this.onScrolling, this);
    }

    public setData(source: Array<any>) {
        this._dataSource = source;
        this._listRenderCount = this.maxRenderCount <= 0 ? 50 : Math.floor(this.maxRenderCount);
        this._start = this._end = 0;
        this.clearList();

        if (this._dataSource && this._dataSource.length > 0) {
            this.listLoading.active = true;
            this.updateList();
        }
    }

    private clearList() {
        for (let i = this._listItemCount - 1; i >= 0; i--) {
            const item = this.listContentNd.children[i];
            if (item) {
                this._itemPool.put(item);
            }
        }
        this._listItemCount = 0;
    }

    private updateList() {
        if (this._end == this._dataSource.length) {
            this._listItemCount = this.listContentNd.children.length;
            this._isUpdating = false;
            this.getComponent(ScrollView)!.scrollToTop();
            this.scheduleOnce(() => {
                this.onScrolling();
            })
            this.listLoading.active = false;
            return;
        }

        this._start = this._end;
        this._end = Math.min(this._end + this._listRenderCount, this._dataSource.length);
        this.renderList(this._start, this._end);
        this._isUpdating = true;
    }

    private renderList(start: number, end: number) {
        for (let i = start; i < end; i++) {
            let item = this._itemPool.get();
            if (!item){
                item =instantiate(this.listItemPrefab);
            }
            if (item){
                item.parent = this.listContentNd;
                item.getComponent(UIOpacity)!.opacity = 255;
                item.getComponent(LIstItem)?.setData(i + "");
            }
        }
    }

    //判断listItem是否在可视区域
    private checkVisible(listItem: Node): boolean {
        return listItem.getComponent(UITransform)!.getBoundingBoxToWorld().intersects(this._scrollViewCollisonBox);
    }

    lateUpdate() {
        if (this._isUpdating) {
            this.updateList();
        }
    }
}
